package elasticsearch

import (
	"context"
	"errors"
	"fmt"
	"go-toy/util"

	"github.com/olivere/elastic/v7"
)

type Proxy struct {
	ESClient *elastic.Client
}

var ESProxy *Proxy

// Get elasticsearch client proxy
func GetProxy() *Proxy {
	if ESProxy == nil {
		ESProxy = &Proxy{}
	}
	return ESProxy
}

// Builded elasticsearch client
func (p *Proxy) BuildClient() *Proxy {
	if p.ESClient == nil {
		var host string = util.Viper.GetString("elasticsearch.host")
		var port string = util.Viper.GetString("elasticsearch.port")
		var url string = fmt.Sprintf("http://%s:%s", host, port)

		ESClient, err := elastic.NewClient(elastic.SetURL(url), elastic.SetSniff(false))
		if err != nil {
			util.UtilPanic(err)
		}

		info, code, err := ESClient.Ping(url).Do(context.Background())
		if err != nil {
			util.UtilPanic(fmt.Errorf("error connecting to Elasticsearch: %s", err))
		}
		util.PrintlnSuccess(fmt.Sprintf("Connected to Elasticsearch cluster %q at %q (Cluster version %d)", info.ClusterName, url, code))

		p.ESClient = ESClient
	}
	return p
}

// Get all indices in Elasticsearch
func (p *Proxy) GetAllIndices() []string {
	if p.ESClient == nil {
		util.UtilPanic(errors.New("ESClient is not initialized"))
	}
	// Use CatIndices API to get all indices
	indices, err := p.ESClient.CatIndices().Do(context.Background())
	if err != nil {
		util.UtilPanic(fmt.Errorf("error connecting to Elasticsearch: %s", err))
	}

	// Extract index names from response
	var indexNames []string
	for _, index := range indices {
		indexNames = append(indexNames, index.Index)
	}

	return indexNames
}

// Get index information in Elasticsearch
func (p *Proxy) GetIndexInfo(index string) map[string]*elastic.IndicesGetResponse {
	if p.ESClient == nil {
		util.UtilPanic(errors.New("ESClient is not initialized"))
	}
	if p.IndexExists(index) {
		// Use Get API to get index information
		result, err := p.ESClient.IndexGet(index).Do(context.Background())
		if err != nil {
			util.UtilPanic(fmt.Errorf("error getting index %s: %s", index, err))
		}
		return result
	} else {
		return nil
	}
}

// Check if index exists in Elasticsearch
func (p *Proxy) IndexExists(index string) bool {
	if p.ESClient == nil {
		util.UtilPanic(errors.New("ESClient is not initialized"))
	}
	// Use IndexExists API to check if index exists
	exists, err := p.ESClient.IndexExists(index).Do(context.Background())
	if err != nil {
		util.UtilPanic(fmt.Errorf("error connecting to Elasticsearch: %s", err))
	}
	return exists
}

// Create index in Elasticsearch
func (p *Proxy) CreatedIndex(index string, bodyJson any) {
	if p.ESClient == nil {
		util.UtilPanic(errors.New("ESClient is not initialized"))
	}
	if !p.IndexExists(index) {
		result, err := p.ESClient.CreateIndex(index).BodyJson(bodyJson).Do(context.Background())
		if err != nil {
			util.UtilPanic(fmt.Errorf("error creating index %s: %s", index, err))
		}
		if !result.Acknowledged {
			util.UtilPanic(fmt.Errorf("not acknowledged by Elasticsearch when creating index"))
		}
	}
}

// Delete index in Elasticsearch
func (p *Proxy) DeleteIndex(index string) {
	if p.ESClient == nil {
		util.UtilPanic(errors.New("ESClient is not initialized"))
	}
	if p.IndexExists(index) {
		result, err := p.ESClient.DeleteIndex(index).Do(context.Background())
		if err != nil {
			util.UtilPanic(fmt.Errorf("error deleting index %s: %s", index, err))
		}
		if !result.Acknowledged {
			util.UtilPanic(fmt.Errorf("not acknowledged by Elasticsearch when deleting index"))
		}
	}
}

// Add document to Elasticsearch index
func (p *Proxy) AddDocument(index string, document any) (result *elastic.IndexResponse) {
	if p.ESClient == nil {
		util.UtilPanic(errors.New("ESClient is not initialized"))
	}
	if !p.IndexExists(index) {
		util.UtilPanic(fmt.Errorf("index %s does not exist", index))
	}
	result, err := p.ESClient.Index().
		Index(index).
		BodyJson(document).
		Do(context.Background())
	if err != nil {
		util.UtilPanic(fmt.Errorf("error adding document into index %s: %s", index, err))
	}
	return
}

// Add some documents to Elasticsearch index
func (p *Proxy) AddDocuments(index string, documents []any) (result *elastic.BulkResponse) {
	if p.ESClient == nil {
		util.UtilPanic(errors.New("ESClient is not initialized"))
	}
	if !p.IndexExists(index) {
		util.UtilPanic(fmt.Errorf("index %s does not exist", index))
	}
	bulkRequest := p.ESClient.Bulk()
	for _, document := range documents {
		bulkRequest.Add(elastic.NewBulkIndexRequest().Index(index).Doc(document))
	}
	result, err := bulkRequest.Do(context.Background())
	if err != nil {
		util.UtilPanic(fmt.Errorf("error adding documents into index %s: %s", index, err))
	}
	return
}

// Search Dodument in Elasticsearch index
func (p *Proxy) SearchDocumentSimple(index string, query elastic.Query) (result *elastic.SearchResult) {
	if p.ESClient == nil {
		util.UtilPanic(errors.New("ESClient is not initialized"))
	}
	if !p.IndexExists(index) {
		util.UtilPanic(fmt.Errorf("index %s does not exist", index))
	}
	result, err := p.ESClient.Search().
		Index(index).
		Query(query).
		Do(context.Background())
	if err != nil {
		util.UtilPanic(fmt.Errorf("error searching document in index %s: %s", index, err))
	}
	return
}
